package edu.towson.cosc.cosc455.astewart.compiler;
import edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer;
/**
 * 
 * @author AndrewStewart
 * Checks to make sure proper tags are used. Builds an abstract syntax tree, reports an error and exits
 * if syntax is incorrect 
 */
public class MySyntaxAnalyzer implements SyntaxAnalyzer {
	int boldVariable=0;
	int paraVariable=0;
	int italVariable=0;
	@Override
	//#HAI head? comment? #KTHXBYE
	public void lolcode() {
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.DOC_BEGIN)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.DOC_BEGIN + " was expected.");
			System.exit(0);
		}else{ 
			MyCompiler.parseTree.add(MyCompiler.token);
			MyCompiler.Lexer.getNextToken();
			if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_BEGIN)){
				define();
			}
			if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.HEAD_BEGIN))
				head();
			else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.COMMENT_BEGIN)){	
				comment();
				head();
			}
			body();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.DOC_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.DOC_END + " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token);
			if (!MyCompiler.Lexer.isComplete()){
				System.err.println("SYNTAX ERROR: Annotations after #KTHXBYE");
				System.exit(0);
			}
				
			}
			
		}

	@Override
	public void comment() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")){
			text();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.COMMENT_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.COMMENT_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token);
			MyCompiler.Lexer.getNextToken();
		}
	}
	
	@Override
	public void head() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.TITLE_BEGIN)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.TITLE_BEGIN+ " was expected.");
			System.exit(0);
		}else{
			title();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.HEAD_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.DOC_END + " was expected.");
			System.exit(0);
		}else{ 
			
			MyCompiler.parseTree.add(MyCompiler.token+" HEAD");
			MyCompiler.Lexer.getNextToken();
		}

	}

	@Override
	public void title() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")){
			text();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.TITLE_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.TITLE_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token + " TITLE");
			MyCompiler.Lexer.getNextToken();
		}

	}

	@Override
	public void paragraph() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_BEGIN)){
			define();
			paraVariable++;
		}
		innerParagraph();
		if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.PARAGRAPH_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.PARAGRAPH_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token + " PARAGRAPH");
			MyCompiler.Lexer.getNextToken();
		}
		for (int i=0; i<paraVariable; i++){
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			
		}
	}
	

	@Override
	public void bold() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_BEGIN)){
			define();
			boldVariable++;
		}
		innerText();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.BOLD_END + " was expected.");
			System.exit(0);
		}else{
		MyCompiler.parseTree.add(MyCompiler.token + " BOLD");
		MyCompiler.Lexer.getNextToken();
		}
		for (int i=0; i<boldVariable; i++){
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			
		}
	}

	@Override
	public void italics() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_BEGIN)){
			define();
			italVariable++;
		}
		innerText();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.ITALICS_END + " was expected.");
			System.exit(0);
		}else{
		MyCompiler.parseTree.add(MyCompiler.token + " ITALICS");
		MyCompiler.Lexer.getNextToken();
		}
		for (int i=0; i<italVariable; i++){
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			MyCompiler.variableTokens.remove(MyCompiler.variableTokens.size()-1);
			
		}
	}

	@Override
	public void list() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_ITEM_BEGIN)){
			System.out.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.ITALICS_END + " was expected.");
			System.exit(0);
		}else 
			item();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.LIST_END + " was expected.");
			System.exit(0);
		}else{
		MyCompiler.parseTree.add(MyCompiler.token + " LIST");
		MyCompiler.Lexer.getNextToken();
		}
	}

	@Override
	public void item() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		innerList();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_ITEM_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.LIST_ITEM_END + " was expected.");
			System.exit(0);
		}else{
		MyCompiler.parseTree.add(MyCompiler.token + " ITEM");
		MyCompiler.Lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_ITEM_BEGIN))
		item();
		}
	}
	
	/**
	 * This method implements the BNF grammar rule for legal annotations inside a list.
	 *
	 * 
	 */
	public void innerList(){
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)){
			bold();
			innerList();
		}
		else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)){
			italics();
			innerList();
		}
		else if(MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN))
			list();	
			innerText();
	}

	@Override
	public void audio() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")){
			text();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.AUDIO_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.AUDIO_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token + " AUDIO");
			MyCompiler.Lexer.getNextToken();
		}

	}

	@Override
	public void video() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")){
			text();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VIDEO_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.VIDEO_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token + " VIDEO");
			MyCompiler.Lexer.getNextToken();
		}

	}

	@Override
	public void define() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")){
			String [] variableName= MyCompiler.token.split(" ");
			if (variableName.length >1 ){
				System.out.println("SYNTAX ERROR: Variable name must be a single word and contain no spaces");
				System.exit(0);
			}else
			variableText();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_CALL)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.VARIABLE_DEFINE_CALL + " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token);
			MyCompiler.Lexer.getNextToken();
			if (!MyCompiler.token.startsWith("#")){
				variableText();
			}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_END)){
				System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.VARIABLE_DEFINE_END+ " was expected.");
				System.exit(0);
			}else{
				MyCompiler.parseTree.add(MyCompiler.token);
				MyCompiler.Lexer.getNextToken();
				if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_DEFINE_BEGIN)){
					define();
				}
			}
		}
	}

	@Override
	public void use() {
		// TODO Auto-generated method stub
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
		
		while(MyCompiler.token.endsWith(" "))
		{
			MyCompiler.token = MyCompiler.token.substring(0, MyCompiler.token.length()-1);
		}
		if (!MyCompiler.token.startsWith("#")){
			String s="";
			for (int i= MyCompiler.variableTokens.size()-1; i>=0 ;i--){
				if(MyCompiler.token.equalsIgnoreCase(MyCompiler.variableTokens.get(i))){
					s=MyCompiler.variableTokens.get(i+1);
					break;
				}
			}
			if(s.equals("")){
				System.err.println("STATIC SEMANTIC ERROR: " + MyCompiler.token + " isn't defined");
				System.exit(0);
			}
			MyCompiler.parseTree.add(s + " ");
			MyCompiler.Lexer.getNextToken();
		}if(!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_USE_END)){
			System.err.println("SYNTAX ERROR: " + MyCompiler.token + " when " + LexicalTokens.COMMENT_END+ " was expected.");
			System.exit(0);
		}else{
			MyCompiler.parseTree.add(MyCompiler.token);
			MyCompiler.Lexer.getNextToken();
		}
	}
	/**
	 * This method implements the BNF grammar rule for any legal annotations inside of the body.
	 * 
	 */
	public void body(){
		innerText();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.COMMENT_BEGIN)){
			comment();
			body();
		}else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.PARAGRAPH_BEGIN)){
			paragraph();
			body();
		}else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)){
			bold();
			body();
		}else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)){
			italics();
			body();
		}else if(MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN)){
			list();
			body();
		}
		
	}
	/**
	 * Adds text to the parse tree
	 */
	public void text(){
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();	
	}
	
	/**
	 * Adds the variable name to a variable token arrayList
	 */
	public void variableText(){
		while(MyCompiler.token.endsWith(" "))
		{
			MyCompiler.token = MyCompiler.token.substring(0, MyCompiler.token.length()-1);
		}
		MyCompiler.variableTokens.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();
	}
	/**
	 * This method calls the the corresponding method for any legal annotations in the inner paragraph
	 *
	 */
	public void innerParagraph(){
		innerText();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)){
			bold();
			innerParagraph();
		}
		else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)){
			italics();
			innerParagraph();
		}
		else if(MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN)){
			list();
			innerParagraph();
		}
		else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.NEWLINE)){
			newLine();
			innerParagraph();
		}
		
	}
	/**
	 * This method calls the the corresponding method for any legal annotations in the inner text
	 *
	 */
	public void innerText(){
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_USE_BEGIN)){
			use();
			innerText();
		}else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.NEWLINE)){
			newLine();
			innerText();
		}else if(MyCompiler.token.equalsIgnoreCase(LexicalTokens.AUDIO_BEGIN)){
			audio();
			innerText();
		}else if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VIDEO_BEGIN)){
			video();
			innerText();
		}else if (!MyCompiler.token.startsWith("#")){
			text();
			innerText();
		}
	}
	/**
	 * This method adds the newline tag to the parse tree
	 *
	 */
	public void newLine(){
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.Lexer.getNextToken();	
	}
}
